// Copyright (c) Appeon. All rights reserved.
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. 
// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.

namespace SnapObjects.Data
{
    internal class ModelConditionBuilder
    {
        private readonly ModelSqlHelper _modelSqlHelper;
        private readonly SqlExpressionBuilder _sqlExpressionBuilder;

        public ModelConditionBuilder(
            ModelSqlHelper modelSqlHelper,
            SqlExpressionBuilder sqlExpressionBuilder)
        {
            _modelSqlHelper = modelSqlHelper;
            _sqlExpressionBuilder = sqlExpressionBuilder;
        }

        public virtual void BuildWhere(ISqlFromBuilder sqlFromBuilder)
        {
            var sqlWheres = _modelSqlHelper.ModelMeta.SqlWheres;

            foreach (var sqlWhere in sqlWheres)
            {
                var left = sqlWhere.RawConditionDefined ? System.String.Empty : this.BuildConditionLeft(sqlWhere);

                var sqlOperator = sqlWhere.Operator;

                switch (sqlOperator)
                {
                    case SqlBinaryOperator.Is:
                    case SqlBinaryOperator.IsNot:
                    {
                        if (sqlWhere.ConditionType == ModelConditionType.Where)
                        {
                            sqlFromBuilder.WhereValue(left, sqlOperator, null);
                        }
                        else if (sqlWhere.ConditionType == ModelConditionType.AndWhere)
                        {
                            sqlFromBuilder.AsWhere().AndWhereValue(left, sqlOperator, null);
                        }
                        else if (sqlWhere.ConditionType == ModelConditionType.OrWhere)
                        {
                            sqlFromBuilder.AsWhere().OrWhereValue(left, sqlOperator, null);
                        }

                        break;
                    }
                    default:
                    {
                        if (sqlWhere.RawConditionDefined)
                        {
                            var condition = _sqlExpressionBuilder.BuildSqlExpression(
                                sqlWhere.RawCondition, buildSubQueris: true, buildParameters: true);

                            if (sqlWhere.ConditionType == ModelConditionType.Where)
                            {
                                sqlFromBuilder.WhereRaw(condition);
                            }
                            else if (sqlWhere.ConditionType == ModelConditionType.AndWhere)
                            {
                                sqlFromBuilder.AsWhere().AndWhereRaw(condition);
                            }
                            else if (sqlWhere.ConditionType == ModelConditionType.OrWhere)
                            {
                                sqlFromBuilder.AsWhere().OrWhereRaw(condition);
                            }
                        }
                        else if (sqlWhere.RightDefined)
                        {
                            var right = this.BuildConditionRight(sqlWhere);

                            if (sqlWhere.ConditionType == ModelConditionType.Where)
                            {
                                sqlFromBuilder.Where(left, sqlOperator, right);
                            }
                            else if (sqlWhere.ConditionType == ModelConditionType.AndWhere)
                            {
                                sqlFromBuilder.AsWhere().AndWhere(left, sqlOperator, right);
                            }
                            else if (sqlWhere.ConditionType == ModelConditionType.OrWhere)
                            {
                                sqlFromBuilder.AsWhere().OrWhere(left, sqlOperator, right);
                            }
                        }

                        break;
                    }
                }
            }
        }

        public virtual void BuildHaving(ISqlFromBuilder sqlFromBuilder)
        {
            var modelMeta = _modelSqlHelper.ModelMeta;

            foreach (var sqlHaving in modelMeta.SqlHavings)
            {
                var left = sqlHaving.RawConditionDefined ? System.String.Empty : this.BuildConditionLeft(sqlHaving);

                if (sqlHaving.ConditionType == ModelConditionType.Having)
                {
                    if (sqlHaving.RawConditionDefined)
                    {
                        var condition = _sqlExpressionBuilder.BuildSqlExpression(
                                                sqlHaving.RawCondition, buildSubQueris: true, buildParameters: true);

                        sqlFromBuilder.HavingRaw(condition);
                    }
                    else if (sqlHaving.RightDefined)
                    {
                        var right = this.BuildConditionRight(sqlHaving);
                        sqlFromBuilder.Having(left, sqlHaving.Operator, right);
                    }
                }
                else if (sqlHaving.ConditionType == ModelConditionType.AndHaving)
                {
                    if (sqlHaving.RawConditionDefined)
                    {
                        var condition = _sqlExpressionBuilder.BuildSqlExpression(
                                                sqlHaving.RawCondition, buildSubQueris: true, buildParameters: true);

                        sqlFromBuilder.AsHaving().AndHavingRaw(condition);
                    }
                    else if (sqlHaving.RightDefined)
                    {
                        var right = this.BuildConditionRight(sqlHaving);
                        sqlFromBuilder.AsHaving().AndHaving(left, sqlHaving.Operator, right);
                    }
                }
                else if (sqlHaving.ConditionType == ModelConditionType.OrHaving)
                {
                    if (sqlHaving.RawConditionDefined)
                    {
                        var condition = _sqlExpressionBuilder.BuildSqlExpression(
                                                sqlHaving.RawCondition, buildSubQueris: true, buildParameters: true);

                        sqlFromBuilder.AsHaving().OrHavingRaw(condition);
                    }
                    else if (sqlHaving.RightDefined)
                    {
                        var right = this.BuildConditionRight(sqlHaving);
                        sqlFromBuilder.AsHaving().OrHaving(left, sqlHaving.Operator, right);
                    }
                }
            }
        }

        protected virtual string BuildConditionLeft(IModelSqlCondition sqlCondition)
        {
            return _sqlExpressionBuilder.BuildSqlExpression(
                        sqlCondition.Left, buildSubQueris: true, buildParameters: true);
        }

        protected virtual string BuildConditionRight(IModelSqlCondition sqlCondition)
        {
            return _sqlExpressionBuilder.BuildSqlExpression(
                        sqlCondition.Right, buildSubQueris: true, buildParameters: true);
        }
    }
}
